Skip to content

docs(lint): security-anchor-high-privilege states its scope — declared everyone suggestions, not guest bindings - #16429

Merged
baozhoutao merged 1 commit into
mainfrom
claude/issue-16110-anchor-rule-scope-statement
Sep 7, 2026
Merged

docs(lint): security-anchor-high-privilege states its scope — declared everyone suggestions, not guest bindings#16429
baozhoutao merged 1 commit into
mainfrom
claude/issue-16110-anchor-rule-scope-statement

Conversation

@baozhoutao

Copy link
Copy Markdown
Contributor

Fixes #16110

What

security-anchor-high-privilege's docblock and rule-catalogue entry did not state its own scope. The rule judges a declared isDefault: true (an everyone-suggested set) at author time — the same predicate the bind-time gate (describeAnchorForbiddenBits(set, 'everyone' | 'guest')) enforces at bind time. It does not, and cannot, see a set an operator binds to guest at install: that decision happens after authoring, and isDefault: true is the only suggestion a package can declare today (suggested-audience-bindings.ts's "the only declarable suggestion" line; ADR-0090 D9 — "a package may suggest bindings to everyone or guest; the admin confirms each individually"). Without a stated scope, the rule id read as coverage it did not have for an app-authored anchor set bound to guest.

Three sites now say so explicitly:

  • the module-header catalogue table row for this rule
  • one paragraph added to the header's "every error rule mirrors a runtime enforcement point" discussion
  • the rule-site comment immediately above the isDefault === true check

Why not a behaviour change

Per the maintainer's ruling on the card (comment 5557093062, adopting recommendation 2): a package-declarable guest suggestion key is refused as a rider here — "adding a package-declarable guest suggestion key is a feature on the manual floor." This PR states the existing scope; it adds no schema key, no new rule, no new finding branch. The retirement-ledger consequence (hotcrm's local assertion for a guest-bound anchor set stays; this rule's row may not be counted as live coverage for it) is already on the card — nothing to do here in another repo.

Diff

Docblock/comment-only — no message/hint string moved, no behaviour changed. skip-changeset (no user-read string moved; the PM applies the label).

Tests

  • pnpm --filter @objectstack/lint exec vitest run --maxWorkers=2 src/validate-security-posture.test.ts — 109 passed (existing suite is the regression control; no new finding, none needed).
  • pnpm --filter @objectstack/lint test (through the shared verify lock) — 100 files / 3444 passed, 5 skipped.
  • pnpm --filter @objectstack/lint typecheck (through the shared verify lock) — clean.
  • All 39 dispatch-derived gate commands for this diff — exit 0 (two needed a build prerequisite first: pnpm --filter '@objectstack/lint...' build for check:docs-transcript-drift, and a full pnpm build — 72/72 tasks — for check:dual-build-cjs-loads; both re-ran green afterward).

🤖 Generated with Claude Code

https://claude.ai/code/session_01Vbw3RPgdtqesx4azk9SbW8


Generated by Claude Code

security-anchor-high-privilege judged declared isDefault:true
(everyone) suggestions only, but nothing in the module header or the
rule-site comment said so — the rule id read as coverage for any
anchor-bound set, including one an operator binds to guest at install
time. That binding is outside what a package-time linter can see and
is held by the bind-time gate (describeAnchorForbiddenBits(set,
'guest')) instead.

Docblock-only: catalogue table row, header paragraph, and the
rule-site comment now state the scope explicitly. No message/hint
string moved, no behaviour changed, no new finding branch.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Vbw3RPgdtqesx4azk9SbW8
@claude claude Bot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Sep 6, 2026
@github-actions github-actions Bot added the size/s label Sep 6, 2026
@github-actions

github-actions Bot commented Sep 6, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/lint, touching 1 documentable anchor(s).

1 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:

  • content/docs/permissions/authorization.mdx (via validateSecurityPosture (symbol, a top-level function))

2 release-owned page(s) also name something this change touched. These are read-only:

  • content/docs/releases/v13.mdx (via validateSecurityPosture (symbol, a top-level function))
  • content/docs/releases/v17.mdx (via validateSecurityPosture (symbol, a top-level function))

content/docs/releases/ is RELEASE-OWNED (AGENTS.md "Documentation Guardrails"): release
notes are written centrally at release time, and a code PR that edits them is the exact PR
that guardrail exists to stop. They are still audited — read-only. If one of them is actually
wrong, file an issue or open a dedicated docs-only PR; do not edit it here.

What this run could not see
  • the SDK route bridge reached 61 of 219 client-bound route-ledger rows — the other 158 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 158: 0 are remediable by widening that discovery convention (an in-repo file declares the path; the convention did not scan it); 56 are structural — on a ledger where NOT ONE row is declared in-repo, so no discovery change reaches them at any price; 102 are undecided (no in-repo declaration, on a ledger that has other in-repo registrars — absence and an unreadable spelling are not distinguishable here). The rows themselves: node scripts/docs-audit/affected-docs.mjs --bridge-coverage
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.

Coarse fallback — 5 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json 0a038cc06d92ba3ae663f9f1e12c52e72dbc7d39packageMentionDocs.

Which tree this was computed on

This run read content/docs from 4b7ef0fbb4e2bfe7137bd112122ea03dac75b9cf — the merge of head 9f7b597e149480e4b256abd928d47819da2b070c into base 0a038cc06d92ba3ae663f9f1e12c52e72dbc7d39, which is what actions/checkout gives a pull_request run. Not the PR head.

A worktree cut from an older main holds a different content/docs, so re-deriving there can legitimately return a different list — that is a different tree, not a wrong row. To answer on the same tree:

# while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 4b7ef0fbb4e2bfe7137bd112122ea03dac75b9cf && git checkout 4b7ef0fbb4e2bfe7137bd112122ea03dac75b9cf
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 0a038cc06d92ba3ae663f9f1e12c52e72dbc7d39 9f7b597e149480e4b256abd928d47819da2b070c && git checkout -B drift-repro 0a038cc06d92ba3ae663f9f1e12c52e72dbc7d39 && git merge --no-ff 9f7b597e149480e4b256abd928d47819da2b070c

node scripts/docs-audit/affected-docs.mjs --json 0a038cc06d92ba3ae663f9f1e12c52e72dbc7d39

⚠️ That checkout carried uncommitted changes, so the commit above does not fully identify what was read.

Advisory only, and a precision-first one (#9192): a page is listed because it names a
symbol, wire route or SDK method this diff touched — not because it mentions a changed
package. Each row says which anchor put it there, so a wrong row is reportable rather than
merely annoying. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs 0a038cc06d92ba3ae663f9f1e12c52e72dbc7d39 → pass the list as
args.docs, on the commit named under Which tree this was computed on.

@claude

claude Bot commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

Queue reading 2026-09-07T00:41Z (PM seat devx, session_01Vbw3RPgdtqesx4azk9SbW8) — still queued, regroup count 1, no removed_from_merge_queue.

  • Timeline: added_to_merge_queue 00:06:28Z at head 9f7b597e1; no removal, no merge.
  • Group run 1 (34068756923, base a7e78f7, created 00:06:47Z): Test Core (5/6) completed/success 00:35:02Z (28 min); Test Core aggregate completed/success 00:35:19Z. Discarded by the regroup below, not by a failure.
  • Regroup at 00:18:53Z → group run 2 (34069422770, base 3e7ef9c): shards 1–4 completed/success by 00:27:26Z; Test Core (5/6) and (6/6) in_progress since 00:19:11Z; Test Core aggregate not started.
  • PR docs(changeset): the IANA time-zone columns changeset declares its accept-set narrowing as BREAKING #16430 queued behind this one at 00:18:54Z on this group's head.

Seat action: none (a regroup is not a removal, per #16173). Next probe at 00:54Z; the seat's one re-queue stays unspent.


Generated by Claude Code

Merged via the queue into main with commit ac76425 Sep 7, 2026
39 checks passed
@baozhoutao
baozhoutao deleted the claude/issue-16110-anchor-rule-scope-statement branch September 7, 2026 00:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/s skip-changeset PR has no user-facing published change; bypasses the changeset gate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

lint: security-anchor-high-privilege keys off permissionSet.isDefault, so an anchor-bound set that does not author that flag is never checked

2 participants